﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ParserCombinatorLibrary
{
    public static class ParserLinqProvider
    {
        public static Parser<R2, I> Select<R1, R2, I>(this Parser<R1, I> p, Func<R1, R2> selector)
        {
            return i =>
            {
                var r = p(i);
                return
                    r == null
                    ? null
                    : new ParserOutput<R2>
                    {
                        Result = selector(r.Result),
                        NewPosition = r.NewPosition,
                    };
            };
        }

        public static Parser<R2, I> SelectMany<R1, R2, I>(this Parser<R1, I> p, Func<R1, Parser<R2, I>> selector)
        {
            return i =>
            {
                var r = p(i);
                return
                    r == null
                    ? null
                    : selector(r.Result)(
                        new ParserInput<I>
                        {
                            Tokens = i.Tokens,
                            CurrentPosition = r.NewPosition
                        }
                        );
            };
        }

        public static Parser<R, I> SelectMany<R1, R2, R, I>(this Parser<R1, I> p, Func<R1, Parser<R2, I>> selector, Func<R1, R2, R> composer)
        {
            return p.SelectMany(r1 => selector(r1).Select(r2 => composer(r1, r2)));
        }

        public static Parser<R, I> Where<R, I>(this Parser<R, I> p, Func<R, bool> predicate)
        {
            return
                from r in p
                from r2 in predicate(r) ? Grammar<I>.Success<R>(r) : Grammar<I>.Fail<R>()
                select r2
                ;
        }

        public static ParserOutput<R> Parse<R, I>(this Parser<R, I> p, I[] input)
        {
            return p(new ParserInput<I> { Tokens = input, CurrentPosition = 0 });
        }
    }
}
